{
  "cells": [
    {
      "cell_type": "markdown",
      "source": [
        "We're going to look into the Runnable - one of the core LangChain primitives, and how to use LCEL (LangChain Expressive Language)."
      ],
      "metadata": {
        "id": "-lZnStGTfgex"
      },
      "id": "-lZnStGTfgex"
    },
    {
      "cell_type": "markdown",
      "source": [
        "# LangChain Runnables"
      ],
      "metadata": {
        "id": "Cuzkefx76Zyb"
      },
      "id": "Cuzkefx76Zyb"
    },
    {
      "cell_type": "code",
      "id": "b2sJ75wlbmcPD1iXfJXgJYdO",
      "metadata": {
        "tags": [],
        "id": "b2sJ75wlbmcPD1iXfJXgJYdO"
      },
      "source": [
        "from langchain_core.runnables import RunnableLambda"
      ],
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "Let's start with a very simple *Runnable* created from a function:"
      ],
      "metadata": {
        "id": "wdCkf4Vz6mHk"
      },
      "id": "wdCkf4Vz6mHk"
    },
    {
      "cell_type": "code",
      "source": [
        "runnable = RunnableLambda(lambda x: x + 1)\n",
        "\n",
        "runnable.invoke(1)"
      ],
      "metadata": {
        "id": "yggCCEpcMkYu",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "executionInfo": {
          "status": "ok",
          "timestamp": 1728719813457,
          "user_tz": -120,
          "elapsed": 1,
          "user": {
            "displayName": "",
            "userId": ""
          }
        },
        "outputId": "b57b4345-644f-48c3-b050-219139f1fe99"
      },
      "id": "yggCCEpcMkYu",
      "execution_count": null,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "2"
            ]
          },
          "metadata": {},
          "execution_count": 18
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "Now we can put together our first chain:"
      ],
      "metadata": {
        "id": "SNIRyH7W6uh7"
      },
      "id": "SNIRyH7W6uh7"
    },
    {
      "cell_type": "code",
      "source": [
        "from typing import Optional\n",
        "from langchain_core.runnables import Runnable, RunnableConfig\n",
        "\n",
        "def increment_by_one(x: int) -> int:\n",
        "  return x + 1\n",
        "\n",
        "def fake_llm(x: int) -> str:\n",
        "  return f\"Result = {x}\"\n",
        "\n",
        "\n",
        "class MyFirstChain(Runnable[int, str]):\n",
        "\n",
        "   def invoke(self, input: str, config: Optional[RunnableConfig] = None) -> str:\n",
        "    increment = increment_by_one(input)\n",
        "    return fake_llm(increment)\n"
      ],
      "metadata": {
        "id": "j-YgLSRxP79h"
      },
      "id": "j-YgLSRxP79h",
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "chain = MyFirstChain()\n",
        "result = chain.invoke(1)\n",
        "print(result)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "y-ndl3NV629R",
        "executionInfo": {
          "status": "ok",
          "timestamp": 1728719655949,
          "user_tz": -120,
          "elapsed": 2,
          "user": {
            "displayName": "",
            "userId": ""
          }
        },
        "outputId": "23a2f5e9-7994-4e59-a065-cc67c3618c6e"
      },
      "id": "y-ndl3NV629R",
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Result = 2\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "And we can do the same much easier with LangChain Expressive Language (LCEL):"
      ],
      "metadata": {
        "id": "JqjXgdKE64r1"
      },
      "id": "JqjXgdKE64r1"
    },
    {
      "cell_type": "code",
      "source": [
        "chain = (\n",
        "    RunnableLambda(increment_by_one)\n",
        "    | RunnableLambda(fake_llm)\n",
        ")\n",
        "\n",
        "\n",
        "result = chain.invoke(1)\n",
        "print(result)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "3SxAN_7tP8AE",
        "executionInfo": {
          "status": "ok",
          "timestamp": 1728719658625,
          "user_tz": -120,
          "elapsed": 407,
          "user": {
            "displayName": "",
            "userId": ""
          }
        },
        "outputId": "11799cb4-4cd7-4b9d-bc5e-d53c244d3cd5"
      },
      "id": "3SxAN_7tP8AE",
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Result = 2\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "Actually, you should only convert the last element explicitly to RunnableLambda, and LangChain would take care about else for you:"
      ],
      "metadata": {
        "id": "koBFjlmFoo-d"
      },
      "id": "koBFjlmFoo-d"
    },
    {
      "cell_type": "code",
      "source": [
        "chain = (\n",
        "    increment_by_one\n",
        "    | RunnableLambda(fake_llm)\n",
        ")\n",
        "\n",
        "\n",
        "result = chain.invoke(1)\n",
        "print(result)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "4RQD7un3omQw",
        "executionInfo": {
          "status": "ok",
          "timestamp": 1728719669737,
          "user_tz": -120,
          "elapsed": 386,
          "user": {
            "displayName": "",
            "userId": ""
          }
        },
        "outputId": "b4ab70a1-3a27-4d41-b902-5fb0fc416c1b"
      },
      "id": "4RQD7un3omQw",
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Result = 2\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "LCEL with | operator is actually equivalent to creating a RunnableSequence explicitly:"
      ],
      "metadata": {
        "id": "ktKvZaWSDcDf"
      },
      "id": "ktKvZaWSDcDf"
    },
    {
      "cell_type": "code",
      "source": [
        "from langchain_core.runnables import RunnableSequence\n",
        "\n",
        "\n",
        "a = (RunnableLambda(increment_by_one) | RunnableLambda(fake_llm))\n",
        "b = RunnableSequence(RunnableLambda(increment_by_one), RunnableLambda(fake_llm))\n",
        "\n",
        "a == b"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "7Il9hIG67KWS",
        "executionInfo": {
          "status": "ok",
          "timestamp": 1728719675053,
          "user_tz": -120,
          "elapsed": 4,
          "user": {
            "displayName": "",
            "userId": ""
          }
        },
        "outputId": "07d0cb4b-a359-4023-d1be-bbedef50678d"
      },
      "id": "7Il9hIG67KWS",
      "execution_count": null,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "True"
            ]
          },
          "metadata": {},
          "execution_count": 8
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "## RunnableParallel"
      ],
      "metadata": {
        "id": "nB9pk7zvDjlX"
      },
      "id": "nB9pk7zvDjlX"
    },
    {
      "cell_type": "markdown",
      "source": [
        "Another powerful LCEL primitive is RunnableParallel. You pass multiple chains as named arguments, and it runs them in parallel and combines their outputs into a dict with keys being argnames and values being outputs:"
      ],
      "metadata": {
        "id": "HNaDfKslDnQU"
      },
      "id": "HNaDfKslDnQU"
    },
    {
      "cell_type": "code",
      "source": [
        "from langchain_core.runnables import RunnableParallel\n",
        "\n",
        "chain = RunnableParallel(step1=increment_by_one | RunnableLambda(fake_llm), step2=fake_llm)\n",
        "\n",
        "chain.invoke(1)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "ZMjHbuPEGzFv",
        "executionInfo": {
          "status": "ok",
          "timestamp": 1728719701493,
          "user_tz": -120,
          "elapsed": 6,
          "user": {
            "displayName": "",
            "userId": ""
          }
        },
        "outputId": "36f3757b-5fc0-4a1c-830f-1a1a452a05d9"
      },
      "id": "ZMjHbuPEGzFv",
      "execution_count": null,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "{'step1': 'Result = 2', 'step2': 'Result = 1'}"
            ]
          },
          "metadata": {},
          "execution_count": 10
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "You can also easily compose chains:"
      ],
      "metadata": {
        "id": "tdPrd0SFk2J3"
      },
      "id": "tdPrd0SFk2J3"
    },
    {
      "cell_type": "code",
      "source": [
        "chain1 = increment_by_one | chain\n",
        "chain1.invoke(1)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "twY0aP3Wk1rL",
        "executionInfo": {
          "status": "ok",
          "timestamp": 1728719704608,
          "user_tz": -120,
          "elapsed": 385,
          "user": {
            "displayName": "",
            "userId": ""
          }
        },
        "outputId": "c91b8a7d-7c8c-4b66-afdb-ae1add6a9877"
      },
      "id": "twY0aP3Wk1rL",
      "execution_count": null,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "{'step1': 'Result = 3', 'step2': 'Result = 2'}"
            ]
          },
          "metadata": {},
          "execution_count": 11
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "You don't need to use the RunnableParallel constructor, you can just combine the chains within a dictionary:"
      ],
      "metadata": {
        "id": "6muSPEW5j-VC"
      },
      "id": "6muSPEW5j-VC"
    },
    {
      "cell_type": "code",
      "source": [
        "from langchain_core.runnables import RunnableParallel\n",
        "\n",
        "chain2 = ( RunnableLambda(increment_by_one)\n",
        "  | {\"step1\": increment_by_one | RunnableLambda(fake_llm), \"step2\": fake_llm}\n",
        ")\n",
        "\n",
        "chain2.invoke(1)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "H_MedXfH7KYe",
        "executionInfo": {
          "status": "ok",
          "timestamp": 1728719713551,
          "user_tz": -120,
          "elapsed": 325,
          "user": {
            "displayName": "",
            "userId": ""
          }
        },
        "outputId": "1cb72e73-d001-441f-92fb-f387b3c3f4b1"
      },
      "id": "H_MedXfH7KYe",
      "execution_count": null,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "{'step1': 'Result = 3', 'step2': 'Result = 2'}"
            ]
          },
          "metadata": {},
          "execution_count": 12
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "print(chain1 == chain2)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "_4ZszelGjbXm",
        "executionInfo": {
          "status": "ok",
          "timestamp": 1728719713551,
          "user_tz": -120,
          "elapsed": 1,
          "user": {
            "displayName": "",
            "userId": ""
          }
        },
        "outputId": "1a759e0c-4fce-4f38-b09d-fdddca0d2b9c"
      },
      "id": "_4ZszelGjbXm",
      "execution_count": null,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "True\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "## itemgetter"
      ],
      "metadata": {
        "id": "P3U9jy49Iq_n"
      },
      "id": "P3U9jy49Iq_n"
    },
    {
      "cell_type": "markdown",
      "source": [
        "We typically pass input as dictionaries, and there's a convinient way to retrieve an element from a dictionary with a built-in *itemgetter* function:"
      ],
      "metadata": {
        "id": "aShrcfUOwWFU"
      },
      "id": "aShrcfUOwWFU"
    },
    {
      "cell_type": "code",
      "source": [
        "from operator import itemgetter\n",
        "\n",
        "chain = (\n",
        "  itemgetter(\"x\")\n",
        "  | RunnableLambda(increment_by_one)\n",
        "  | fake_llm\n",
        ")\n",
        "\n",
        "\n",
        "chain.invoke({\"x\": 1})"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 35
        },
        "id": "qBznqy-UIr_o",
        "executionInfo": {
          "status": "ok",
          "timestamp": 1728719724085,
          "user_tz": -120,
          "elapsed": 365,
          "user": {
            "displayName": "",
            "userId": ""
          }
        },
        "outputId": "07d00983-4291-437b-b22a-fc08e08d3042"
      },
      "id": "qBznqy-UIr_o",
      "execution_count": null,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "'Result = 2'"
            ],
            "application/vnd.google.colaboratory.intrinsic+json": {
              "type": "string"
            }
          },
          "metadata": {},
          "execution_count": 15
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "## RunnablePassThrough"
      ],
      "metadata": {
        "id": "J9RZqvhkp3Tb"
      },
      "id": "J9RZqvhkp3Tb"
    },
    {
      "cell_type": "markdown",
      "source": [
        "And we can modify dictionaries in-place with Runnables (by assigning values into a dictionary or create new dictionaries). That's how we produce an output dictionary:"
      ],
      "metadata": {
        "id": "bmPFH3d8whPv"
      },
      "id": "bmPFH3d8whPv"
    },
    {
      "cell_type": "code",
      "source": [
        "from langchain_core.runnables import RunnablePassthrough\n",
        "\n",
        "chain_rps = RunnableParallel(\n",
        "    origin=RunnablePassthrough(),\n",
        "    output=increment_by_one\n",
        ")\n",
        "\n",
        "chain_rps.invoke(1)"
      ],
      "metadata": {
        "id": "IKhu5TxFJvkh",
        "executionInfo": {
          "status": "ok",
          "timestamp": 1728719725684,
          "user_tz": -120,
          "elapsed": 2,
          "user": {
            "displayName": "",
            "userId": ""
          }
        },
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "568122f6-0769-4d59-e5df-ede68cb3686e"
      },
      "id": "IKhu5TxFJvkh",
      "execution_count": null,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "{'origin': 1, 'output': 2}"
            ]
          },
          "metadata": {},
          "execution_count": 16
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "Now let's create a new dictionary by adding (assigning) additional values to it:"
      ],
      "metadata": {
        "id": "rweY_Q_eHkBT"
      },
      "id": "rweY_Q_eHkBT"
    },
    {
      "cell_type": "code",
      "source": [
        "chain_assign = RunnablePassthrough().assign(y=itemgetter(\"x\") | RunnableLambda(increment_by_one))\n",
        "\n",
        "query = {\"x\": 1}\n",
        "result = chain_assign.invoke({\"x\": 1})\n",
        "print(result)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "_DftSnSuI177",
        "executionInfo": {
          "status": "ok",
          "timestamp": 1728726318723,
          "user_tz": -120,
          "elapsed": 4,
          "user": {
            "displayName": "",
            "userId": ""
          }
        },
        "outputId": "a8359b56-7e7d-4fa3-aedc-b8fb7954a278"
      },
      "id": "_DftSnSuI177",
      "execution_count": 19,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "{'x': 1, 'y': 2}\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "And let's put it all together by creating a more complex dictionary (with sub-chains):"
      ],
      "metadata": {
        "id": "p7bUB4MOHrD-"
      },
      "id": "p7bUB4MOHrD-"
    },
    {
      "cell_type": "code",
      "source": [
        "chain_rps = RunnableParallel(\n",
        "    origin=RunnablePassthrough().assign(length=lambda input: input[\"x\"]+1),\n",
        "    modified=lambda input: increment_by_one(input[\"x\"])\n",
        ")\n",
        "\n",
        "chain_rps.invoke({\"x\": 1})"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "m43_mgUFp-Gi",
        "executionInfo": {
          "status": "ok",
          "timestamp": 1728726368155,
          "user_tz": -120,
          "elapsed": 346,
          "user": {
            "displayName": "",
            "userId": ""
          }
        },
        "outputId": "4e8de85c-da26-4753-c171-d609504dfd2f"
      },
      "id": "m43_mgUFp-Gi",
      "execution_count": 20,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "{'origin': {'x': 1, 'length': 2}, 'modified': 2}"
            ]
          },
          "metadata": {},
          "execution_count": 20
        }
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.10.10"
    },
    "colab": {
      "provenance": [],
      "name": "Chapter 1"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}